package io.xxx.xbutler.web.config;

import com.alibaba.fastjson2.JSON;
import io.xxx.xbutler.common.ApiResult;
import io.xxx.xbutler.common.ApplicationException;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@Slf4j
@RestControllerAdvice
public class UniformResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(@Nonnull MethodParameter returnType, @Nonnull Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.hasMethodAnnotation(RequestMapping.class);
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  @Nonnull MethodParameter returnType,
                                  @Nonnull MediaType selectedContentType,
                                  @Nonnull Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  @Nonnull ServerHttpRequest request,
                                  @Nonnull ServerHttpResponse response) {

        Object result = switch (body) {
            case ApiResult _ -> body;
            case ApplicationException e -> ApiResult.fail(e);
            case Throwable t -> ApiResult.fail(t);
            case null, default -> ApiResult.success(body);
        };
        if (selectedConverterType.isAssignableFrom(StringHttpMessageConverter.class)) {
            return JSON.toJSONString(result);
        } else {
            return result;
        }
    }

    @ExceptionHandler(Throwable.class)
    public ApiResult exceptionHandler(@Nonnull Throwable t) {
        log.error(t.getMessage(), t);
        return ApiResult.fail(t);
    }

    @ExceptionHandler(ApplicationException.class)
    public ApiResult exceptionHandler(@Nonnull ApplicationException e) {
        log.error(e.getMessage(), e);
        return ApiResult.fail(e);
    }
}
